#include "SudokuSolver.h"

SudokuSolver::SudokuSolver(void)
{
   // Create 9x9 grid of tiles.
   myGrid.resize(81);

   // Assign each tile a unique Row, Column and Box number.
   for (int x = 0; x < 81; x++)
   {
      int Row = (int) x / 9;
      int Col = x - (Row * 9);
      int Box = (3*(int)(Row / 3)) + (int)(Col / 3);
      myGrid[x].setRowNum(Row+1);
      myGrid[x].setColNum(Col+1);
      myGrid[x].setBoxNum(Box+1);
   }
}

SudokuSolver::SudokuTile& SudokuSolver::at(const int row, const int col)
{
   return myGrid[((row-1) * 9) + (col-1)];
}

void SudokuSolver::dumpValues()
{
   for (int x = 1; x <= 9; x++)
   {
      for (int y = 1; y <= 9; y++)
      {
         for (int z = 1; z <= 9; z++)
         {
            if (at(x,y).isPossible(z))
               printf("%d", z);
         }
         if (y == 3 || y == 6)
            printf("\t ");
         else
            printf("\t");
      }
      if (x == 3 || x == 6)
         printf("\n\n");
      else
         printf("\n");
   }
   printf("\n\n");
}

bool SudokuSolver::findUniquePossibles()
{
   bool returnVal = false;

   for (int x = 0; x < 81; x++)
   {
      if (myGrid[x].getValue() > 0)
         continue;

      int lRow = myGrid[x].getRowNum();
      int lCol = myGrid[x].getColNum();
      int lBox = myGrid[x].getBoxNum();

      for (int i = 1; i <= 9; i++)
      {
         if (myGrid[x].isPossible(i))
         {
            bool uniqueRowVal = true;
            bool uniqueColVal = true;
            bool uniqueBoxVal = true;

            for (int y = 0; y < 81; y++)
            {
               if (x != y && myGrid[y].isPossible(i))
               {
                  if (myGrid[y].getRowNum() == lRow)
                     uniqueRowVal = false;

                  if (myGrid[y].getColNum() == lCol)
                     uniqueColVal = false;

                  if (myGrid[y].getBoxNum() == lBox)
                     uniqueBoxVal = false;
               }
            }

            if (uniqueRowVal || uniqueColVal || uniqueBoxVal)
            {
               myGrid[x].setValue(i);
               returnVal = true;
               break;
            }
         }
      }
   }

   return returnVal;
}

SudokuSolver::TileType SudokuSolver::getTileVal(int row, int col) const
{
   return myGrid[((row-1) * 9) + (col-1)].getValue();
}

bool SudokuSolver::processKnownValues()
{
   bool returnVal = false;

   for (int x = 0; x < 81; x++)
   {
      // Do we need these?  Throughput vs memory
      //int lRow = myGrid[x].getRowNum();
      //int lCol = myGrid[x].getColNum();
      //int lBox = myGrid[x].getBoxNum();

      for (int y = 0; y < 81; y++)
      {
         if (x != y && myGrid[y].getValue() != 0)
         {
            if (myGrid[y].getRowNum() == myGrid[x].getRowNum() ||
                myGrid[y].getColNum() == myGrid[x].getColNum() ||
                myGrid[y].getBoxNum() == myGrid[x].getBoxNum())
            {
               returnVal |= myGrid[x].removePossible(myGrid[y].getValue());
            }
         }
      }
   }
   return returnVal;
}

void SudokuSolver::setTileVal(const int row, const int col, const int val)
{
   myGrid[((row-1) * 9) + (col-1)].setValue(val);
}

void SudokuSolver::Solve()
{
   bool keepGoing = false;
   do
   {
      keepGoing = false;
      keepGoing |= processKnownValues();
      keepGoing |= findUniquePossibles();
      keepGoing |= processTriangulations();
      keepGoing |= processNakedPairs();
   } while (keepGoing);
   dumpValues();
}

bool SudokuSolver::processTriangulations()
{
   bool returnVal = false;

   for (int x = 0; x < 81; x++)
   {
      if (myGrid[x].numPossible() == 2)
      {
         for (int y = 0; y < 81; y++)
         {
            if (x != y && myGrid[x].comparePossible(myGrid[y]))
            {
               if (myGrid[x].getRowNum() != myGrid[y].getRowNum() && 
                   myGrid[x].getColNum() != myGrid[y].getColNum())
               {
                  for (int z = 1; z <= 9; z++)
                  {
                     if (myGrid[x].isPossible(z))
                     {
                        returnVal |= this->at(myGrid[x].getRowNum(), myGrid[y].getColNum()).removePossible(z);
                        returnVal |= this->at(myGrid[y].getRowNum(), myGrid[x].getColNum()).removePossible(z);
                     }
                  }
               }
            }
         }
      }
   }
   return returnVal;
}

bool SudokuSolver::processNakedPairs()
{
   bool success = false;
   for (int x = 0; x < 81; x++)
   {
      for (int y = 0; y < 81; y++)
      {
         if (myGrid[x].comparePossible(myGrid[y]) && myGrid[x].numPossible() == 2 && x != y)
         {
            if (myGrid[x].getRowNum() == myGrid[y].getRowNum())
            {
               for (int z = 0; z < 81; z++)
               {
                  if (myGrid[z].getRowNum() == myGrid[x].getRowNum() &&
                      z != x && z != y)
                  {
                     for (int i = 1; i <= 9; i++)
                     {
                        if (myGrid[x].isPossible(i))
                           success |= myGrid[z].removePossible(i);
                     }
                  }
               }
            }
            if (myGrid[x].getColNum() == myGrid[y].getColNum())
            {
               for (int z = 0; z < 81; z++)
               {
                  if (myGrid[z].getColNum() == myGrid[x].getColNum() &&
                      z != x && z != y)
                  {
                     for (int i = 1; i <= 9; i++)
                     {
                        if (myGrid[x].isPossible(i))
                           success |= myGrid[z].removePossible(i);
                     }
                  }
               }
            }
            if (myGrid[x].getBoxNum() == myGrid[y].getBoxNum())
            {
               for (int z = 0; z < 81; z++)
               {
                  if (myGrid[z].getBoxNum() == myGrid[x].getBoxNum() &&
                      z != x && z != y)
                  {
                     for (int i = 1; i <= 9; i++)
                     {
                        if (myGrid[x].isPossible(i))
                           success |= myGrid[z].removePossible(i);
                     }
                  }
               }
            }
         }
      }
   }
   if (success)
      printf("Got one\n");
   return success;
}